package com.lijie.ljblog.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lijie.ljblog.common.BlogConstants;
import com.lijie.ljblog.mapper.MusicMapper;
import com.lijie.ljblog.mapper.WebSiteVisitMapper;
import com.lijie.ljblog.model.*;
import com.lijie.ljblog.model.vo.ljpage.BingImgVO;
import com.lijie.ljblog.service.UtilService;
import com.lijie.ljblog.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Title: ljblog_springboot
 * @Description: ()
 * @Author:lijie
 * @Version:1.1.0
 * @since 2021/11/4
 */
@Service
@Slf4j
public class UtilServiceImpl implements UtilService {
    @Value("${tenxunMapKey}")
    String tenxunKey;
    @Value("${tenxunMapHttp}")
    String tenxunHttp;
    @Value("${hefengKey}")
    String hefengKey;
    @Value("${hefeng3dHttp}")
    String hefeng3dHttp;
    @Value("${hefengNowHttp}")
    String hefengNowHttp;
    @Value("${historyTodayHttp}")
    String historyTodayHttp;
    @Value("${bingImg.API}")
    String bingImgAPI;
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    WebUtils webUtils;
    @Resource
    MusicMapper musicMapper;
    @Resource
    WebSiteVisitMapper webSiteVisitMapper;

    @Autowired
    RedisUtils redisUtils;

    @Value("${fileUploadUrl}")
    String fileDownloadPath;

    public SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    public static final String bingUrl="https://cn.bing.com";
    /**
     * @Title: getLocationByIP
     * @Description: (调用地图API, 通过ip获得城市定位)
     * @Author:lijie
     * @Version:1.1.0
     * @since 2021/11/5 11:02
     */
    @Override
    public Result<Map<String,Object>> getLocationByIP(String ip) {
        Result<Map<String,Object>> result=new Result<>();
        Map<String,Object> map=new HashMap<>();
        try{
            //百度地图：https://api.map.baidu.com/location/ip?&coor=bd09ll&ak=Nbr60aM3zSyhXbSuB6YcGPWENONMxLGT&ip=122.97.199.48
            //高德地图：https://restapi.amap.com/v5/ip?ip=112.87.128.60&key=b140305b7fd1cba953bf38a5188e94f0&type=4
            //https://apis.map.qq.com/ws/location/v1/ip?ip=112.87.128.60&key=KZWBZ-2YWCS-FM4OX-6I4VQ-4XRQS-YQFKE
            ResponseEntity<String> forEntity = restTemplate.getForEntity(tenxunHttp + tenxunKey + "&ip=" + ip,String.class);//object.getClass().getClasses()
            HashMap res = JSONObject.parseObject(forEntity.getBody(), HashMap.class);
            if(res!=null){
                String status = MapUtils.getString(res, "status");
                if(StringUtils.isNotBlank(status)&&"0".equals(status)){
                    Map addressResult = MapUtils.getMap(res, "result");
                    Map location = MapUtils.getMap(addressResult, "location");//经纬度
                    Map address = MapUtils.getMap(addressResult, "ad_info");//地址信息
                    StringBuilder addressBuilder=new StringBuilder();
                    addressBuilder.append(MapUtils.getString(address,"nation")).append("-")
                            .append(MapUtils.getString(address,"province")).append("-")
                            .append(MapUtils.getString(address,"city")).append("-")
                            .append(MapUtils.getString(address,"district"));
                    map.put("address",addressBuilder.toString());
                    map.put("wd",MapUtils.getString(location,"lat"));
                    map.put("jd",MapUtils.getString(location,"lng"));
                    result.setSuccess();
                    result.setData(map);
                }else{
                    result.setErrored(MapUtils.getString(res,"message"));
                }
            }
        }catch (ClassCastException e){
            result.setErrored("腾讯地图API 获取地址失败");
        }
        return result;
    }

    /**
     * @Title: getLocationByRequset
     * @Description: (通过request 获得请求定位城市)
     * @Author:lijie
     * @Version:1.1.0
     * @since 2021/11/5 11:03
     */
    @Override
    public Result<Map<String, Object>> getLocationByRequset(HttpServletRequest request) {
        String ipByRequset = WebUtils.getIpByRequset(request);
        return this.getLocationByIP(ipByRequset);
    }

    /**
     * @Title: getClimateByRequst
     * @Description: ( )
     * @Author:lijie
     * @since 2021/11/9 14:05
     * @Version:1.1.0
     * @return:   climate:封装的天气结果
     */
    @Override
    public Result<Climate> getClimateByRequst(HttpServletRequest request) {
        Result<Climate> result=new Result<>();
        try{
            //通过request请求获得ip地址
            String ip = WebUtils.getIpByRequset(request);
            System.out.println("IP是"+ip);
            //通过ip获得大致定位
            result= this.getClimateByIP(ip);
        }catch (Exception e){
            result.setErrored("天气获取出错了");
        }
        return result;
    }

    /**
     * @Title: getClimateByRequst
     * @Description: (通过ip或的天气信息)
     * @Author:lijie
     * @Version:1.1.0
     * @return: climate:封装的天气结果
     * @since 2021/11/9 14:05
     */
    @Override
    public Result<Climate> getClimateByIP(String IP) {
        Result<Climate> result = new Result<>();
        Climate climate = null;
        try {
            //通过ip获得大致定位
            Result<Map<String, Object>> locationByIP = this.getLocationByIP(IP);
            if(locationByIP!=null){
                if(locationByIP.isSuccess()){
                    Map<String, Object> data = locationByIP.getData();
                    if(MapUtils.isNotEmpty(data)){
                        String jd = MapUtils.getString(data, "jd");
                        String wd = MapUtils.getString(data, "wd");
                        HttpHeaders httpHeaders = new HttpHeaders();
                        // Accept 表示客户端支持什么格式的响应体
                        httpHeaders.set("contentType", "application/json;charset=UTF-8");
                        // Accept-Encoding 头，表示客户端可以接收gzip格式的压缩
                        httpHeaders.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
                        //未来三天天气接口
                        ResponseEntity<byte[]> forEntity = restTemplate.exchange
                                (hefeng3dHttp + hefengKey + "&location=" +jd+","+wd
                                        , HttpMethod.GET, new HttpEntity<>(httpHeaders), byte[].class);
                        if(forEntity.getStatusCode()== HttpStatus.OK){
                            // 获取服务器响应体编码
                            String contentEncoding = forEntity.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
                            if ("gzip".equals(contentEncoding)) { // gzip编码
                                // gzip解压服务器的Body响应体
                                byte[] weatherData = WebUtils.unGZip(
                                        new ByteArrayInputStream(Objects.requireNonNull(forEntity.getBody())));
                                String weatherJson = new String(weatherData, StandardCharsets.UTF_8);
                                climate = JSONObject.parseObject(weatherJson, Climate.class);
                                if(climate!=null){
                                    climate.setCity(MapUtils.getString(data,"address"));
                                }
                            } else {
                                // todo 其他的编码
                                result.setErrored("和风API响应值编码不是Gzip，请联系我，谢谢");
                            }
                        }else{
                            result.setErrored("和风API响应出错了");
                        }
                        //今天天气接口
                        ResponseEntity<byte[]> nowWeather = restTemplate.exchange
                                (hefengNowHttp + hefengKey + "&location=" +jd+","+wd
                                        , HttpMethod.GET, new HttpEntity<>(httpHeaders), byte[].class);
                        if(nowWeather.getStatusCode()== HttpStatus.OK){
                            // 获取服务器响应体编码
                            String contentEncoding = nowWeather.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
                            if ("gzip".equals(contentEncoding)) { // gzip编码
                                // gzip解压服务器的Body响应体
                                byte[] weatherData = WebUtils.unGZip(
                                        new ByteArrayInputStream(Objects.requireNonNull(nowWeather.getBody())));
                                String weatherJson = new String(weatherData, StandardCharsets.UTF_8);
                                NowWeather nowWeatherResult= JSONObject.parseObject(weatherJson, NowWeather.class);
                                if(climate != null&&nowWeatherResult!=null){
                                    climate.setNowWeather(nowWeatherResult);
                                    result.setSuccess();
                                    result.setData(climate);
                                }else{
                                    result.setErrored("API调用错误：未来三天天气对象为空");
                                }
                            } else {
                                // todo 其他的编码
                                result.setErrored("和风API响应值编码不是Gzip，请联系我，谢谢");
                            }
                        }else{
                            result.setErrored("和风API响应出错了");
                        }
                    }
                }else{
                    result.setErrored(locationByIP.getMessage());
                }
            }else{
                result.setErrored("腾讯地图API返回值为空");
            }
        } catch (Exception e) {
            System.out.println(e);
            result.setErrored("天气获取出错了");
        }
        return result;
    }

    /**
     * @Title:
     * @Description: (文件上传)
     * @Author:lijie
     * @Version:1.1.0
     * @since 2021/11/30 16:28
     */
    @Override
    public String fileUpload(MultipartFile file, String path) throws IOException {
        //原文件名

        String datePrefix = dateFormat.format(new Date());
        File folder = new File(path+datePrefix);

        if(!folder.isDirectory()){
            folder.mkdirs();
        }
        String originFileName = file.getOriginalFilename();
        //新的文件名
        String splitName=originFileName.lastIndexOf(".")==-1?
                originFileName: originFileName.substring(originFileName.lastIndexOf("."));
        String newFileName = UUID.randomUUID() + splitName;
        file.transferTo(new File(folder, newFileName));
        String returnPath = "/" + datePrefix +"/"+ newFileName;

        return returnPath;
    }
    public String fileUpload(File file, String path) throws IOException {
        //原文件名

        String datePrefix = dateFormat.format(new Date());
        File folder = new File(path+datePrefix);

        if(!folder.isDirectory()){
            folder.mkdirs();
        }
        String originFileName = file.getName();
        //新的文件名
        String splitName=originFileName.lastIndexOf(".")==-1?
                originFileName: originFileName.substring(originFileName.lastIndexOf("."));
        String newFileName = UUID.randomUUID() + splitName;
//        file.transferTo(new File(folder, newFileName));
        String returnPath = "/" + datePrefix +"/"+ newFileName;

        return returnPath;
    }

    /**
     * @Title: updateHistoday
     * @Description: (更新调用接口 历史上的今天)
     * @Author:lijie
     * @Version:1.1.0
     * @since 2021/11/30 16:24
     */
    @Override
    public Result<HistoryToday> updateHistoday() {
        Result<HistoryToday> result = new Result<>();
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            // Accept 表示客户端支持什么格式的响应体
            httpHeaders.set("contentType", "application/json;charset=UTF-8");
            // Accept-Encoding 头，表示客户端可以接收gzip格式的压缩
            httpHeaders.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
            ResponseEntity<byte[]> histodayData = restTemplate.exchange
                    (historyTodayHttp, HttpMethod.GET, new HttpEntity<>(httpHeaders), byte[].class);
            if (histodayData.getStatusCode() == HttpStatus.OK) {
                // 获取服务器响应体编码
                String contentEncoding = histodayData.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING);
                byte[] weatherData = null;
                if ("gzip".equals(contentEncoding)) {
                    weatherData = WebUtils.unGZip(
                            new ByteArrayInputStream(Objects.requireNonNull(histodayData.getBody())));
                } else {
                    weatherData=histodayData.getBody();
//                    result.setErrored("历史上的今天接口返回不是Gzip压缩");
                }
                assert weatherData != null;
                String hsitoryTodayJson = new String(weatherData, StandardCharsets.UTF_8);
                HistoryToday historyToday = JSONObject.parseObject(hsitoryTodayJson, HistoryToday.class);
                if (historyToday != null) {
                    List<Common> result1 = historyToday.getResult();
                    result1.remove(result1.size()-1);
                    historyToday.setResult(result1);
                    result.setSuccess();
                    result.setData(historyToday);
                }
            } else {
                result.setErrored("历史上的今天接口请求未成功");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 随机生成一对RSA 公钥秘钥；秘钥上传redis;返回公钥
     * @param redisKey  上传Redis的key
     * @return 公钥
     */
    @Override
    public Result<String> getRSAPublicKey(String redisKey) {
        Result<String> result=new Result<>();
        try {
            Map<Integer, String> integerStringMap = RSAEncrypt.genKeyPair();
            String pubKey = MapUtils.getString(integerStringMap, 0);//公钥
            String priKey = MapUtils.getString(integerStringMap, 1);//私钥
            redisUtils.setEx(redisKey,priKey,10, TimeUnit.MINUTES);//上传redis,10分钟有效期
            result.setSuccess();
            result.setData(pubKey);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (Exception e){
            result.setErrored(e.getMessage());
        }
        return result;
    }

    /**
     * 获得文件下载头
     *
     * @return
     */
    @Override
    public String getFileHeadPath() {
        return fileDownloadPath;
    }

    @Override
    public void musicUploadList(MultipartHttpServletRequest mulRequest, String path) throws Exception {

        MultipartFile musicFile = mulRequest.getFile("music");
        //上传音乐文件，获得相对路径
        String musicFilePath = this.fileUpload(musicFile, path);
        File file = new File(path+musicFilePath);
        //mp3文件转Music对象
        Music musicInfo = MusicUtils.getMusicInfo(file);
        //如果数据库中存在同名歌曲并且歌手相同，停止上传
        QueryWrapper<Music> musicQueryWrapper = new QueryWrapper<>();
        musicQueryWrapper
                .eq("musicname",musicInfo.getName())
                .eq("artist",musicInfo.getArtist());

        List<Music> music = musicMapper.selectList(musicQueryWrapper);
        if(!CollectionUtils.isEmpty(music)){
            //删除file文件
            file.delete();
            throw  new RuntimeException("已存在歌曲，无需上传");
        }
        //下载相对路径
        musicInfo.setUrl(musicFilePath);
        //封面
        byte[] mp3Image = MusicUtils.getMp3Image(file);
        //设置封面名称
        String musicImageName=UUID.randomUUID().toString()+".jpg";
        //保存封面
        FileUtils.saveFile(file.getParentFile().getPath(),musicImageName,mp3Image);
        String musicImageParentPath = musicFilePath.substring(0, musicFilePath.lastIndexOf("/"));
        musicInfo.setCover(musicImageParentPath+musicImageName);

        musicMapper.insert(musicInfo);



    }
    /**
     * 网站访问量+1。并返回访问量
     * @param request
     * @return
     */
    @Override
    public Result<Long> webSiteVisits(HttpServletRequest request) {
        Long aLong = redisUtils.incrBy(BlogConstants.WEBSITE_VISITS, 1);
        webVisitLog(request,"Blog");
        return Result.ok(aLong);
    }

    @Override
    @Cacheable(cacheNames = "ljPage", key = "#day" )
    public String bingDailyImg(String day) {

        StringBuilder result=new StringBuilder(bingUrl);

        ResponseEntity<String> forEntity = restTemplate.getForEntity(bingImgAPI, String.class);
        BingImgVO bingImgVO = JSON.parseObject(forEntity.getBody(), BingImgVO.class);

        if(bingImgVO!=null && CollUtil.isNotEmpty(bingImgVO.getImages())){
            result.append(bingImgVO.getImages().get(0).getUrl());
        }

        return result.toString();


    }

    @Override
    public void webVisitLog(HttpServletRequest request, String type) {
        WebSiteVisit device = WebUtils.getDevice(request.getHeader("User-Agent"));
        device.setWebType(type);
        //获取IP
        String ip = WebUtils.getIpByRequset(request);
        //根据IP获取地址
        Result<Map<String, Object>> locationByIP = this.getLocationByIP(ip);
        if(locationByIP.isError()){
            device.setIpaddress(locationByIP.getMessage());
        }else{
            Map<String, Object> data = this.getLocationByIP(ip).getData();
            device.setIpaddress(MapUtils.getString(data,"address"));
        }
        device.setIp(ip);
        webSiteVisitMapper.insert(device);
    }
}

